import json
import re
from ast import literal_eval
from typing import Any, List, Union


class ParseError(Exception):
    """解析异常类，用于处理解析过程中的错误"""

    def __init__(self, err_msg: str):
        self.err_msg = err_msg


class BaseParser:
    """
    基础解析器类
    
    用于处理动作的输入和输出
    
    参数:
        action (BaseAction): 要验证的动作实例
        
    属性:
        PARAMETER_DESCRIPTION (str): 声明LLM在生成工具参数时应遵循的输入格式
    """

    PARAMETER_DESCRIPTION: str = ''

    def __init__(self, action):
        self.action = action
        self._api2param = {}  # API名称到参数列表的映射
        self._api2required = {}  # API名称到必需参数的映射
        
        # 执行基本的参数验证
        if action.description:
            for api in action.description.get('api_list',
                                            [action.description]):
                # 构建API完整名称
                name = (f'{action.name}.{api["name"]}'
                       if self.action.is_toolkit else api['name'])
                       
                # 获取必需参数和所有参数集合
                required_parameters = set(api['required'])
                all_parameters = {j['name'] for j in api['parameters']}
                
                # 验证必需参数是否都在参数列表中
                if not required_parameters.issubset(all_parameters):
                    raise ValueError(
                        f'unknown parameters for function "{name}": '
                        f'{required_parameters - all_parameters}')
                        
                # 添加参数描述
                if self.PARAMETER_DESCRIPTION:
                    api['parameter_description'] = self.PARAMETER_DESCRIPTION
                    
                # 更新API映射
                api_name = api['name'] if self.action.is_toolkit else 'run'
                self._api2param[api_name] = api['parameters']
                self._api2required[api_name] = api['required']

    def parse_inputs(self, inputs: str, name: str = 'run') -> dict:
        """
        解析LLM为动作生成的输入
        
        参数:
            inputs (str): 从响应中提取的输入字符串
            name (str): API名称，默认为'run'
            
        返回:
            dict: 处理后的输入
        """
        inputs = {self._api2param[name][0]['name']: inputs}
        return inputs

    def parse_outputs(self, outputs: Any) -> List[dict]:
        """
        解析动作返回的输出
        
        参数:
            outputs (Any): 动作的原始输出
            
        返回:
            List[dict]: 处理后的输出，每个元素都是包含'type'和'content'的字典
        """
        if isinstance(outputs, dict):
            outputs = json.dumps(outputs, ensure_ascii=False)
        elif not isinstance(outputs, str):
            outputs = str(outputs)
            
        # 转换为GBK编码以处理特殊字符
        return [{
            'type': 'text',
            'content': outputs.encode('gbk', 'ignore').decode('gbk')
        }]


class JsonParser(BaseParser):
    """
    JSON解析器
    
    将输入字符串转换为字典的解析器
    """

    PARAMETER_DESCRIPTION = (
        'If you call this tool, you must pass arguments in '
        'the JSON format {key: value}, where the key is the parameter name.')

    def parse_inputs(self,
                    inputs: Union[str, dict],
                    name: str = 'run') -> dict:
        """
        解析JSON格式的输入
        
        参数:
            inputs: JSON字符串或字典
            name: API名称
            
        返回:
            dict: 解析后的参数字典
            
        异常:
            ParseError: 当JSON格式无效或参数不匹配时抛出
        """
        if not isinstance(inputs, dict):
            try:
                # 尝试提取和解析JSON
                match = re.search(r'^\s*(```json\n)?(.*)\n```\s*$', inputs,
                                re.S)
                if match:
                    inputs = match.group(2).strip()
                inputs = json.loads(inputs)
            except json.JSONDecodeError as exc:
                raise ParseError(f'invalid json format: {inputs}') from exc
                
        # 验证参数
        input_keys = set(inputs)
        all_keys = {param['name'] for param in self._api2param[name]}
        
        # 检查未知参数
        if not input_keys.issubset(all_keys):
            raise ParseError(f'unknown arguments: {input_keys - all_keys}')
            
        # 检查必需参数
        required_keys = set(self._api2required[name])
        if not input_keys.issuperset(required_keys):
            raise ParseError(
                f'missing required arguments: {required_keys - input_keys}')
                
        return inputs


class TupleParser(BaseParser):
    """
    元组解析器
    
    将输入字符串转换为元组的解析器
    """

    PARAMETER_DESCRIPTION = (
        'If you call this tool, you must pass arguments in the tuple format '
        'like (arg1, arg2, arg3), and the arguments are ordered.')

    def parse_inputs(self,
                    inputs: Union[str, tuple],
                    name: str = 'run') -> dict:
        """
        解析元组格式的输入
        
        参数:
            inputs: 元组字符串或元组
            name: API名称
            
        返回:
            dict: 解析后的参数字典
            
        异常:
            ParseError: 当元组格式无效或参数数量不匹配时抛出
        """
        if not isinstance(inputs, tuple):
            try:
                inputs = literal_eval(inputs)
            except Exception as exc:
                raise ParseError(f'invalid tuple format: {inputs}') from exc
                
        # 验证参数数量
        if len(inputs) < len(self._api2required[name]):
            raise ParseError(
                f'API takes {len(self._api2required[name])} required positional '
                f'arguments but {len(inputs)} were given')
        if len(inputs) > len(self._api2param[name]):
            raise ParseError(
                f'API takes {len(self._api2param[name])} positional arguments '
                f'but {len(inputs)} were given')
                
        # 将位置参数转换为命名参数
        inputs = {
            self._api2param[name][i]['name']: item
            for i, item in enumerate(inputs)
        }
        return inputs
